package org.hertsig.magiccardmarket.xml;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import com.google.common.base.Charsets;

@Slf4j
public class XmlMarshaller {
	public static void marshal(Object element, OutputStream outputStream) {
		marshal(element, new OutputStreamWriter(outputStream));
	}
	
	@SneakyThrows({JAXBException.class, IOException.class})
	public static void marshal(Object element, Writer writer) {
		Marshaller marshaller = JAXBContext.newInstance(element.getClass()).createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		marshaller.setEventHandler(new ErrorLogger());
		marshaller.marshal(element, writer);
		writer.flush();
	}
	
	public static <T> T unmarshal(InputStream inputStream, Class<T> entityType) {
		return unmarshal(new InputStreamReader(inputStream, Charsets.UTF_8), entityType);
	}
	
	@SneakyThrows(JAXBException.class)
	public static <T> T unmarshal(Reader reader, Class<T> entityType) {
		Unmarshaller marshaller = JAXBContext.newInstance(entityType).createUnmarshaller();
		marshaller.setEventHandler(new ErrorLogger());
		return entityType.cast(marshaller.unmarshal(reader));
	}
	
	private static final class ErrorLogger implements ValidationEventHandler {
		@Override
		public boolean handleEvent(ValidationEvent event) {
			ValidationEventLocator locator = event.getLocator();
			if (event.getLinkedException() != null) {
				log.warn("At line {} row {}: ", locator.getLineNumber(), locator.getColumnNumber(), event.getLinkedException());
			}
			else {
				log.warn("At line {} row {}: {}", locator.getLineNumber(), locator.getColumnNumber(), event.getMessage());
			}
			return true;
		}
	}
}
